<template>
  <div class="map-box">
    <div class="map" id="map"></div>
    <div class="btns-box">
      <el-button type="primary" size="small" @click="measure('LineString')">测量</el-button>
      <el-button type="primary" size="small" @click="measure('Polygon')">测面</el-button>
      <el-button type="primary" size="small" @click="clearMeasure">清除测量轨迹</el-button>
    </div>
  </div>
</template>

<script>
import "ol/ol.css";
import Draw from "ol/interaction/Draw";
import Map from "ol/Map";
import Overlay from "ol/Overlay";
import View from "ol/View";
import TileArcGISRest from "ol/source/TileArcGISRest";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { LineString, Polygon } from "ol/geom";
import { OSM, Vector as VectorSource } from "ol/source";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { getArea, getLength } from "ol/sphere";
import { unByKey } from "ol/Observable";

import CONSTANT from "@/config/constant.js";

let layerIndex = 0;

export default {
  name: "measureLengthAndArea",

  data() {
    return {
      map: null,
      raster: null,
      vector: null,
      sketch: null, //Currently drawn feature
      helpTooltipElement: null, //The help tooltip element.
      helpTooltip: null, //Overlay to show the help messages.
      measureTooltipElement: null, //The measure tooltip element.
      measureTooltip: null, //Overlay to show the measurement.
      // Message to show when the user is drawing a polygon.
      continuePolygonMsg: "Click to continue drawing the polygon",
      // Message to show when the user is drawing a line
      continueLineMsg: "Click to continue drawing the line",
      draw: null, //global so we can remove it later
      listener: null,
      shapeType: null,
      drawCache: {}
    };
  },

  methods: {
    init() {
      this.vector = new VectorLayer({
        source: new VectorSource(),
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.2)"
          }),
          stroke: new Stroke({
            color: "#ffcc33",
            width: 2
          }),
          image: new CircleStyle({
            radius: 7,
            fill: new Fill({
              color: "#ffcc33"
            })
          })
        })
      });

      //初始化地图
      this.map = new Map({
        layers: [
          new TileLayer({
            source: new TileArcGISRest({
              url: CONSTANT.GIS_URL
            })
          }),
          this.vector
        ],
        target: "map",
        view: new View({
          center: [113.0567, 23.67537],
          maxZoom: 18,
          minZoom: 8,
          zoom: 13,
          projection: "EPSG:4326"
        })
      });
    },

    //测距、测面
    measure(type) {
      this.shapeType = type;
      layerIndex++;
      this.drawCache[layerIndex] = {
        feature: null,
        measureTooltip: null
      };
      if (this.draw) {
        this.map.removeInteraction(this.draw);
      }
      // 添加map事件
      this.addMapEvent();
    },

    //清除测量
    clearMeasure() {
      for (let i in this.drawCache) {
        this.map.removeOverlay(this.drawCache[i].measureTooltip);
        this.vector.getSource().removeFeature(this.drawCache[i].feature);
      }
      this.drawCache = {};
      layerIndex = 0;
    },

    addMapEvent() {
      this.map.on("pointermove", evt => {
        this.draw
          ? this.pointerMoveHandler(evt)
          : this.map.removeOverlay(this.helpTooltip);
      });
      this.map.getViewport().addEventListener("mouseout", () => {
        this.helpTooltipElement &&
          this.helpTooltipElement.classList.add("hidden");
      });
      this.addInteraction();
    },

    addInteraction() {
      this.draw = new Draw({
        source: this.vector.getSource(),
        type: this.shapeType,
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.2)"
          }),
          stroke: new Stroke({
            color: "rgba(0, 0, 0, 0.5)",
            lineDash: [10, 10],
            width: 2
          }),
          image: new CircleStyle({
            radius: 5,
            stroke: new Stroke({
              color: "rgba(0, 0, 0, 0.7)"
            }),
            fill: new Fill({
              color: "rgba(255, 255, 255, 0.2)"
            })
          })
        })
      });

      this.map.addInteraction(this.draw);
      this.createMeasureTooltip();
      this.createHelpTooltip();
      this.drawHandler();
    },

    /**
     * Handle pointer move.
     * @param {import("../src/ol/MapBrowserEvent").default} evt The event.
     */
    pointerMoveHandler(evt) {
      if (evt.dragging) {
        return;
      }
      /** @type {string} */
      var helpMsg = "Click to start drawing";

      if (this.sketch) {
        var geom = this.sketch.getGeometry();
        if (geom instanceof LineString) {
          helpMsg = this.continueLineMsg;
        }
      }

      this.helpTooltipElement.innerHTML = helpMsg;
      this.helpTooltip.setPosition(evt.coordinate);

      this.helpTooltipElement.classList.remove("hidden");
    },
    /**
     * Format length output.
     * @param {LineString} line The line.
     * @return {string} The formatted length.
     */
    formatLength(line) {
      const sourceProj = this.map.getView().getProjection(); //获取投影坐标系
      var length = getLength(line, { projection: sourceProj });
      var output;
      if (length > 100) {
        output = Math.round((length / 1000) * 100) / 100 + " " + "km";
      } else {
        output = Math.round(length * 100) / 100 + " " + "m";
      }
      return output;
    },

    /**
     * Format area output.
     * @param {Polygon} polygon The polygon.
     * @return {string} Formatted area.
     */
    formatArea(polygon) {
      const sourceProj = this.map.getView().getProjection(); //获取投影坐标系
      const geom = polygon.clone().transform(sourceProj, "EPSG:3857");
      const area = getArea(geom);

      let output;
      if (area > 10000) {
        output =
          Math.round((area / 1000000) * 100) / 100 + " " + "km<sup>2</sup>";
      } else {
        output = Math.round(area * 100) / 100 + " " + "m<sup>2</sup>";
      }
      return output;
    },

    /**
     * Creates a new measure tooltip
     */
    createMeasureTooltip() {
      if (this.measureTooltipElement) {
        this.measureTooltipElement.parentNode.removeChild(
          this.measureTooltipElement
        );
      }
      this.measureTooltipElement = document.createElement("div");
      this.measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
      this.measureTooltip = new Overlay({
        element: this.measureTooltipElement,
        offset: [0, -15],
        positioning: "bottom-center"
      });
      this.map.addOverlay(this.measureTooltip);
    },

    /**
     * Creates a new help tooltip
     */
    createHelpTooltip() {
      if (this.helpTooltipElement) {
        this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
      }
      this.helpTooltipElement = document.createElement("div");
      this.helpTooltipElement.className = "ol-tooltip hidden";
      this.helpTooltip = new Overlay({
        element: this.helpTooltipElement,
        offset: [15, 0],
        positioning: "center-left"
      });
      this.map.addOverlay(this.helpTooltip);
    },

    drawHandler() {
      this.draw.on("drawstart", evt => {
        this.sketch = evt.feature;
        let tooltipCoord = evt.coordinate;

        this.listener = this.sketch.getGeometry().on("change", evt => {
          let output;
          const geom = evt.target;
          if (geom instanceof LineString) {
            output = this.formatLength(geom);
            tooltipCoord = geom.getLastCoordinate();
          } else if (geom instanceof Polygon) {
            output = this.formatArea(geom);
            tooltipCoord = geom.getInteriorPoint().getCoordinates();
          }
          let closeBtn =
            "<i class='tooltip-close-btn tooltip-close-btn_"+layerIndex+"' data-index='" +
            layerIndex +
            "'>×</i>";
          this.measureTooltipElement.innerHTML = output + closeBtn;
          this.measureTooltip.setPosition(tooltipCoord);
          this.drawCache[layerIndex].measureTooltip = this.measureTooltip;
        });
      });

      this.draw.on("drawend", evt => {
        this.drawCache[layerIndex].feature = evt.feature;

        this.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
        this.measureTooltip.setOffset([0, -7]);
        // unset sketch
        this.sketch = null;
        // unset tooltip so that a new one can be created
        this.measureTooltipElement = null;
        this.createMeasureTooltip();
        unByKey(this.listener);
        this.map.removeInteraction(this.draw);
        this.draw = null;

        // 删除图层
        const self = this;
        document.querySelector(".tooltip-close-btn_"+layerIndex).addEventListener("click", function() {
          self.vector.getSource().removeFeature(self.drawCache[this.dataset.index].feature)
          self.map.removeOverlay(self.drawCache[this.dataset.index].measureTooltip);
          delete self.drawCache[this.dataset.index]
        });

      });
    }
  },

  mounted() {
    this.init();
  }
};
</script>

<style lang="less" scoped>
.map-box {
  height: 100%;
  position: relative;
  .map {
    height: 100%;
  }
  .btns-box {
    position: absolute;
    right: 20px;
    top: 20px;
  }
}
.map {
  /deep/ .ol-tooltip {
    position: relative;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 4px;
    color: white;
    padding: 4px 14px 4px 8px;
    opacity: 0.7;
    white-space: nowrap;
    font-size: 12px;
  }
  /deep/ .ol-tooltip-measure {
    opacity: 1;
    font-weight: bold;
  }
  /deep/ .ol-tooltip-static {
    background-color: #ffcc33;
    color: black;
    border: 1px solid white;
  }
  /deep/ .ol-tooltip-measure:before,
  /deep/ .ol-tooltip-static:before {
    border-top: 6px solid rgba(0, 0, 0, 0.5);
    border-right: 6px solid transparent;
    border-left: 6px solid transparent;
    content: "";
    position: absolute;
    bottom: -6px;
    margin-left: -5px;
    left: 50%;
  }
  /deep/ .ol-tooltip-static:before {
    border-top-color: #ffcc33;
  }
  //测面、测距的样式
  /deep/ .tooltip-close-btn {
    font-style: normal;
    position: absolute;
    color: #2d8cf0;
    right: 0px;
    top: -7px;
    font-weight: bold;
    font-size: 15px;
    cursor: pointer;
    &:hover {
      color: #fff;
    }
  }
}
</style>